package main

import (
	"fmt"
	"math"
)

func main() {
	InterFaces() // 接口
	InterVar()   // 接口值
	InterNil()   //底层值为 nil 的接口值
	InterEmpty() //空接口
	InterAsser() //类型断言
	InterChose() //类型选择
}

// 接口类型的定义为一组方法签名
// 接口类型的变量可以持有任何实现了这些方法的值
func InterFaces() {
	var in I = T{"hello"}
	in.M() // print => hello
}

// 任何实现 M() 的类型都可以使用
type I interface {
	M()
}

type T struct {
	S string
}

// 类型 T 实现了接口 I
func (t T) M() {
	fmt.Println("T -> ", t.S)
}

//===========================================================\\

// 接口也是值。它们可以像其它值一样传递。接口值可以用作函数的参数或返回值;
// 接口值可以看做包含值和具体类型的元组 -> (value, type);
// nil 接口值既不保存值也不保存具体类型;
// nil 接口调用方法会产生运行时错误，因为接口的元组内并未包含能够指明该调用哪个具体方法的类型;
func InterVar() {
	fmt.Println()
	var i IVar

	i = &Tvar{"hello"}
	fmt.Printf("(%v, %T)\n", i, i)
	i.M()

	i = F(math.Pi)
	fmt.Printf("(%v, %T)\n", i, i)
	i.M()
}

type IVar interface {
	M()
}

type Tvar struct {
	S string
}

func (t *Tvar) M() {
	fmt.Println("Tvar ->", t.S)
}

type F float64

func (f F) M() {
	fmt.Println("Tvar ->", f)
}

//====================================================\\

// 即便接口内的具体值为 nil，方法仍然会被 nil 接收者调用;
// 注意: 保存了 nil 具体值的接口其自身并不为 nil
func InterNil() {
	fmt.Println()

	var i Inil
	var t *Tnil

	i = t
	fmt.Printf("(%v, %T)\n", i, i)
	i.M()

	i = &Tnil{"hello"}
	fmt.Printf("(%v, %T)\n", i, i)
	i.M()

}

type Inil interface {
	M()
}

type Tnil struct {
	S string
}

func (t *Tnil) M() {
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println("Tnil ->", t.S)
}

//=======================================================================================\\

// 指定了零个方法的接口值被称为 *空接口：*
// 空接口可保存任何类型的值.(因为每个类型都至少实现了零个方法)
// 空接口被用来处理未知类型的值.例如 fmt.Print 可接受类型为 interface{} 的任意数量的参数
func InterEmpty() {
	fmt.Println()

	var i interface{}

	fmt.Printf("(%v, %T)\n", i, i)

	i = 43
	fmt.Printf("(%v, %T)\n", i, i)

	i = "hello world"
	fmt.Printf("(%v, %T)\n", i, i)

}

//==================================================================================\\

// 类型断言提供了访问接口值底层具体值的方式;
// 若 接口 并未保存 要求 类型的值，该语句就会触发一个 panic。
// 为了判断一个接口值是否保存了一个特定的类型，类型断言可返回两个值：其底层值以及一个报告断言是否成功的布尔值。则不会 panic
func InterAsser() {
	fmt.Println()

	var i interface{} = "hello"

	s := i.(string) //返回 string底层值 hello
	fmt.Println(s)

	s, ok := i.(string) //返回 hello 和 true
	fmt.Println(s, ok)

	//f := i.(float64)
	//fmt.Println(f) <=  panic: interface conversion: interface {} is string, not float64

	//ok 将为 false 而 f 将为 T 类型的零值，程序并不会产生 panic。
	f, ok := i.(float64)
	fmt.Println(f, ok) // 0 false
}

//==============================================================================\\

// 类型选择 是一种按顺序从几个类型断言中选择分支的结构。
// 类型选择与一般的 switch 语句相似，不过类型选择中的 case 为类型（而非值），它们针对给定接口值所存储的值的类型进行比较;
func InterChose() {
	fmt.Println()

	do(21)
	do("hello")
	do(true)
}

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("二倍的 %v 是 %v\n", v, v*2)
	case string:
		fmt.Printf("%q 长度为 %v 字节\n", v, len(v))
	default:
		fmt.Printf("我不知道类型 %T\n", v)
	}
}
